AWS CDKでAPIGateway/SQSと連携した2層LambdaをTypeScriptで開発してみる
どうも、もこ@札幌オフィスです。
最近CDKにお熱で、Lambda/API GatewayをAWS SAMを利用して定義していた所に代わり、CDKを使ったプロジェクトがもっと普及してもいいんじゃないかな?と思い、AWS CDK(Typescript)を利用してサーバーレスアプリケーションを開発してみました。
作る物
APIGateway / Lambdaを利用して、SQSにキューイングする一般的なサーバーレスアプリケーションを作ってみます。
API Gateway/Lambdaの構成だけでは味気ないので、SQSをイベントソースに処理用のLambdaを実行する部分も作成し、LambdaはTypeScriptで開発できるようにしてきます!
利用するパッケージ
利用するパッケージはこんな感じです。 @aws-cdk/aws-lambda-nodejs
でTypeScriptのLambda関数を扱い、 @aws-cdk/aws-lambda-event-sources
でSQSからのイベントでLambdaが実行されるように設定します。
@aws-cdk/core: CDKのコア @aws-cdk/aws-sqs: SQSを作るのに使用 @aws-cdk/aws-apigateway: API Gatewayを作成するのに利用 @aws-cdk/aws-lambda: Lambdaのランタイム指定に使用 @aws-cdk/aws-lambda-nodejs: CDKでLambdaを扱うために使用 まだexperimentalなので注意 @aws-cdk/aws-lambda-event-sources: SQSをイベントソースにしたLambdaを作成するために利用 @aws-cdk/aws-iam: IAMのポリシーを定義するのに利用
プロジェクトツリー
作成したプロジェクトのツリーはこんな感じになりました。
Lambda関数ごとにディレクトリを作成しています。
. ├── README.md ├── bin │ └── cdk-event.ts ├── lambda # Lambdaは関数ごとにディレクトリを変えて保存、関数ごとにpackage.jsonを保持 │ ├── backend │ │ ├── index.ts │ │ ├── package-lock.json │ │ └── package.json │ └── front │ ├── index.ts │ ├── package-lock.json │ └── package.json ├── lib │ └── cdk-event-stack.ts # CDKのメイン部分 ├── package-lock.json ├── package.json ├── cdk.json └── tsconfig.json
CDK
CDKのメイン部分はこんな感じで、コメントアウトを入れているので多く見えますが、実態は数十行ちょっとで定義しています。
コードの詳細はコメントアウトをご参照ください。
import { Stack, Construct, StackProps } from '@aws-cdk/core'; import { Queue } from '@aws-cdk/aws-sqs'; import { Runtime } from '@aws-cdk/aws-lambda'; import { NodejsFunction } from '@aws-cdk/aws-lambda-nodejs'; import { SqsEventSource } from '@aws-cdk/aws-lambda-event-sources'; import { LambdaRestApi } from '@aws-cdk/aws-apigateway'; import { PolicyStatement } from '@aws-cdk/aws-iam'; export class CdkEventStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); // フロントのLambda const frontLambda = new NodejsFunction(this, 'FrontLambda', { entry: 'lambda/front/index.ts', handler: 'handler', runtime: Runtime.NODEJS_12_X }); // REST API作成、Lambda指定 const RestAPI = new LambdaRestApi(this, 'FrontAPI', { handler: frontLambda }); // SQS作成 const queue = new Queue(this, 'queue', {}); // バックエンドのLambda const backendLambda = new NodejsFunction(this, 'BackendLambda', { entry: 'lambda/backend/index.ts', handler: 'handler', runtime: Runtime.NODEJS_12_X }); // フロントのLambdaにSQSのURLを環境変数で渡す frontLambda.addEnvironment('QUEUE_URL', queue.queueUrl); // SQSポリシー追加 frontLambda.addToRolePolicy(new PolicyStatement({ actions: ['sqs:SendMessage'], resources: [queue.queueArn] })); // SQSをイベントソースにLambdaを実行するよう設定 const eventSource = backendLambda.addEventSource(new SqsEventSource(queue)); } }
@aws-cdk/aws-apigateway
でAPI Gateway, Lambdaの紐付けも数行で簡単にでき、 @aws-cdk/aws-lambda-nodejs
でTypeScriptのトランスコンパイルまでまるっとやってくれて、 @aws-cdk/aws-lambda-event-sources
で1行でSQSイベントソース設定ができます。
便利な時代になりましたね。
Lambda関数をTypeScriptにするメリット
TypeScriptを使えるようになったことで、 @types/aws-lambda
を使って型がある平和な世界に来ることができました。
import { APIGatewayProxyEvent, APIGatewayEventRequestContext, APIGatewayProxyResult } from 'aws-lambda'; import * as AWS from 'aws-sdk'; const SQS = new AWS.SQS({ region: 'ap-northeast-1' }); const QueueUrl = process.env.QUEUE_URL || ''; export async function handler( event: APIGatewayProxyEvent, context: APIGatewayEventRequestContext ): Promise<APIGatewayProxyResult> { try { const sendMessage = await SQS.sendMessage({ MessageBody: JSON.stringify(event.body), QueueUrl }).promise(); return { statusCode: 200, body: JSON.stringify(sendMessage) }; } catch (error) { console.log(error); return { statusCode: 502, body: JSON.stringify({ message: error }) }; } }
aws-lambda-nodejsの仕組みについてはAWS CDKでLambda Function用のTypeScriptのバンドルを簡単に行うをご参照ください。
@aws-cdk/aws-lambda-event-sourcesで使えるイベントソース
CDKと@aws-cdk/aws-lambda-event-sources
を使うと何も考えなくても連携でて最高です。
2020年5月時点で下記の主要5個のサービスが対応しており、最終的にはサポートされているイベントソースすべてで対応することを目標にしているそうです!
- SQS
- S3
- SNS
- DynamoDB Streams
- Kinesis
まとめ
これまでAWS SAMを利用してきましたが、(もちろん状況によるとは思いますが)AWS CDKの方が簡単に、わかりやすく柔軟にサーバーレスアプリケーションを管理、開発、デプロイすることができるようになりました。
LambdaのTypeScript化もトランスコンパイルするスクリプトを別で実行して、、、などの組み込みが不要になり、より簡単にモダンな感じで開発していくことができます。
めちゃめちゃ簡単に出来てしまって内容が短くなってしまいましたが、今回作成したものはGitHubで公開しているので、どんな感じで管理できるのかを試してみたい方は、是非一度ご覧ください!
https://github.com/mokonist/cdk-sqs-serverless-project